# -*- coding: utf-8 -*-
""" 
叮咚抢菜脚本
==========
通过将移动设备连接至电脑，以自动化测试的方式在设备上模拟点击，从而实现自动抢菜。

@author  : Arnie
@license : (c) 2022 Arnie, MIT License 
"""
import re
import argparse
import time

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.appiumby import AppiumBy

import uiautomator2 as u2
import Play_mp3 as mp3


# 默认设备名称: iOS 或 Android
device_name = 'iOS'
# 默认设备编号: iOS UDID 或 Android的设备号
device_code = ''
# WebDriver URL (仅用于iOS)
driver_url = 'http://169.254.75.26:8100'


# 连接设备
def connect_device():
    if device_name == 'iOS':
        desired_caps = dict()
        desired_caps['platformName'] = 'iOS'
        desired_caps['automationName'] = 'XCUITest'
        desired_caps['udid'] = device_code
        driver = webdriver.Remote(driver_url, desired_caps)
    else:
        driver = u2.connect(device_code)
        if not driver.uiautomator.start():
            driver.uiautomator.start()
            time.sleep(1)

    return driver


# 获取元素
def get_element(driver, name, fuzzy = False):
    if device_name == 'iOS':
        if not fuzzy:
            try:
                return driver.find_element(MobileBy.NAME, name)
            except Exception:
                return None
        else: 
            xpath = '//XCUIElementTypeButton[contains(@name,"%s")]' % name
            try:
                return driver.find_element(AppiumBy.XPATH, xpath)
            except Exception:
                return None
    else:
        element = driver(text=name) if not fuzzy else driver(textContains=name)
        return element if element.exists else None


# 选择送达时间
def select_time_range(driver, try_times = 0, max_retry = 0):
    ## 没有运力，尝试捡漏
    def retry(try_times, max_retry):
        if (max_retry==0 or try_times==max_retry):
            mp3.play('sounds/fail.mp3')
            if max_retry == 0:
                message = '\n😞 没有运力了，进入捡漏阶段\n'
            else:
                message = '\n😞 捡漏失败，是否继续？\n'
            print(message)
            chance = input('请输入尝试次数（0: 放弃捡漏）：')
            if int(chance) <= 0: 
                exit()
            else: 
                try_times = 0
                max_retry = int(chance)
                print('\n')
        if (try_times > 0):
            print('尝试捡漏', try_times, '次')
        return { 'try_times': try_times, 'max_retry':max_retry }

    if device_name == 'iOS':
        xpath = '//XCUIElementTypeOther/XCUIElementTypeOther[2]'
        xpath += '/XCUIElementTypeTable[2]/XCUIElementTypeCell'
        selectable = False

        try:
            items = driver.find_elements(MobileBy.XPATH, xpath)
            items_count = len(items)
            if items_count == 0:
                driver.find_element(AppiumBy.ACCESSIBILITY_ID,'关闭').click()
            for i in range( items_count ):
                item = items[i]
                if not get_element(item,'已约满'):
                    selectable = True
                    print('👌 有运力，加油！')
                    item.click()
                    if get_element(driver, '立即支付'):
                        print('点击 “立即支付”')
                        get_element(driver, '立即支付').click()
                if not selectable and i == items_count-1 :
                    retry = retry(try_times, max_retry)
                    try_times = retry['try_times']
                    max_retry = retry['max_retry']
                    driver.find_element(AppiumBy.ACCESSIBILITY_ID,'关闭').click()
                    driver.find_element(AppiumBy.ACCESSIBILITY_ID,'返回').click()
        except Exception: None

    else:
        xpath_mode = '//*[@resource-id="com.yaya.zone:id/%s"]'
        selector_xpath = xpath_mode % 'rv_selected_hour'
        close_xpath = xpath_mode % 'iv_dialog_select_time_close'
        back_xpath = xpath_mode % 'iv_order_back'
        selector = driver.xpath(selector_xpath)

        if not selector.exists:
            driver.xpath(close_xpath).click_exists(timeout=1)
        else:
            items_count = selector.get(timeout=1).info.get('childCount', 0)
            item_xpath_mode = selector_xpath+'/android.view.ViewGroup[%d]'
            selectable = False
            for i in range(items_count):
                item = driver.xpath(item_xpath_mode % (i+1))
                if not item.exists:
                    continue
                if item.get(timeout=1).info.get('enabled','') != 'false':
                    selectable = True
                    print('👌 有运力，加油！')
                    item.click_exists(timeout=1)
                    if get_element(driver, '立即支付'):
                        print('点击 “立即支付”')
                        get_element(driver, '立即支付').click()
                if not selectable and i == items_count-1 :
                    retry = retry(try_times, max_retry)
                    try_times = retry['try_times']
                    max_retry = retry['max_retry']
                    driver.xpath(close_xpath).click_exists(timeout=1)
                    driver.xpath(back_xpath).click_exists(timeout=1)

    return { 'try_times':try_times+1, 'max_retry':max_retry }


# 抢菜
def grab_food(test = False):
    driver = connect_device()
    start = time.time()
    count = 1
    fails = 1
    try_times = 0
    max_retry = 0

    while True:
        begin = time.time()

        if get_element(driver, '去结算(', True):
            print('点击 “去结算”')
            get_element(driver, '去结算(', True).click()

        if get_element(driver, '我知道了'):
            print('点击 “我知道了”')
            get_element(driver, '我知道了').click()

        if get_element(driver, '重新加载'):
            print('点击 “重新加载”')
            get_element(driver, '重新加载').click()

        if get_element(driver, '请选择送达时间'):
            print('重新选择送达时间')
            get_element(driver, '请选择送达时间').click()

        if get_element(driver, '下单失败'):
            print('下单失败，第', fails, '次')
            fails += 1
            if get_element(driver, '返回购物车'):
                print('点击 “返回购物车”')
                get_element(driver, '返回购物车').click()
        else:
            if get_element(driver, '立即支付'):
                print('点击 “立即支付”')
                get_element(driver, '立即支付').click()

            if get_element(driver, '选择送达时间'):
                print('选择送达时间')
                try_select = select_time_range(driver, try_times, max_retry)
                try_times = try_select['try_times']
                max_retry = try_select['max_retry']

        # 支付宝
        if get_element(driver, '确认交易'):
            print('点击 “确认交易”')
            get_element(driver, '确认交易').click()
            mp3.play('sounds/success.mp3')
            print('\n✌️ “抢菜”成功，退出脚本\n')
            exit()

        # 微信支付
        if get_element(driver, '确认支付'):
            print('点击 “确认支付”')
            get_element(driver, '确认支付').click()
            mp3.play('sounds/success.mp3')
            print('\n✌️ “抢菜”成功，退出脚本\n')
            exit()

        duration = '%.6f' % (time.time()-begin)
        total_duration = '%.6f' % ((time.time()-start) / 60)
        print ('本次操作时间:', duration, '秒')
        print ('总共耗费时间:', total_duration, '分钟，第', count, '次')

        if test and time.time()-start >= 60:
            print('\n测试结束，退出脚本\n')
            exit()

        count += 1


# 运行
def run(test = False):
    print('\n开始“抢菜” >>>\n')
    mp3.play('sounds/start.mp3')

    while True:
        try:
            grab_food(test)
        except Exception as e:
            print(e)
            mp3.play('sounds/fail.mp3')
            time.sleep(5)


# 值守
def wait():
    print('\n开始值守任务，请确认已取消电脑的自动休眠功能\n')
    driver = connect_device()
    if device_name == 'iOS':
        touchend = 560; duration = 100
    else:
        touchend = 960; duration = 0.1

    while True:
        hour = time.localtime().tm_hour
        minute = time.localtime().tm_min

        if hour>22 or hour<5 or (hour==5 and minute<55):
            driver.swipe(200,360, 200,touchend, duration)
            time.sleep(15)
        else:
            run()


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='manual to this script')
    parser.add_argument('--device', type=str, default='')
    parser.add_argument('--code', type=str, default='')
    args = parser.parse_args()

    if args.device: device_name = args.device
    if args.code: device_code = args.code

    if device_name not in ['iOS', 'Android']:
        print('\n⚠️  设备名称仅限填写 iOS 和 Android\n')
        exit()
    if not re.search('^[\w-]{8,}|[\d+\.]{3}\d+:\d+$', device_code):
        print('\n⚠️  设备编号格式错误\n')
        exit()

    hour = time.localtime().tm_hour
    minute = time.localtime().tm_min

    if hour>22 or hour<5 or (hour==5 and minute<55):
        tips =  '\n现在还没到“抢菜”时间，请选择您的策略：\n'
        tips += '\n 1. 程序值守任务，5:55 开始自动“抢菜”   2. 测试60秒   3. 退出程序\n'
        print(tips)
        plan = input('请输入您的选项：')

        if plan == '1':
            wait()
        else:
            if plan == '2':
                run(True)
            else:
                print('\n')
                exit()

    run()
